package org.swxjava.assembler.avm1.convert;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.swxjava.assembler.Converter;
import org.swxjava.assembler.MarshallException;
import org.swxjava.assembler.ObjectCompiler;
import org.swxjava.util.BeanUtils;
import org.swxjava.util.Log;
import org.swxjava.util.LogFactory;

public class BeanConverter extends MapConverter implements Converter {

	/** Logger */
	private static final Log LOG = LogFactory.getLog(BeanConverter.class);

	public ObjectCompiler convert(Object data) throws MarshallException {
		Map map = null;
		Map<Object,Boolean> conversionContext = new HashMap<Object,Boolean>();
		try {
			map = toMap(conversionContext, data);
		} catch (Exception e) {
			LOG.error("Could not convert bean : " + data, e);
			throw new MarshallException(data.getClass(), e);
		}
		return convertMapEntries(map);
	}

	public static final Map toMap(Map<Object,Boolean> conversionContext, Object bean) throws Exception {
		if(bean == null) {
			return null;
		} else {
			Map map = new HashMap<String, Object>();
			Class targetClass = bean.getClass();
			BeanInfo beanInfo = Introspector.getBeanInfo(targetClass);
			for(PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
				if(pd != null && pd.getReadMethod() != null && !pd.getName().equals("class")) {
					String propName = pd.getName();
					Object propValue = pd.getReadMethod().invoke(bean, new Object[] {});
					// FIXME More inteligence needs here...
					if(propValue != null 
							&& !(propValue instanceof String) 
							&& !BeanUtils.isPrimitive(propValue.getClass())
							&& !BeanUtils.isArray(propValue) 
							&& !(propValue instanceof Collection) 
							&& !(propValue instanceof List) 
							&& !(propValue instanceof Map)
							&& !(propValue instanceof Date || propValue instanceof java.sql.Date || propValue instanceof Timestamp)) {
						// recursive
						if(conversionContext.containsKey(propValue)) {
							// TODO resolve reference implementation
							LOG.debug(" - reference for the bean property ... " + targetClass.getSimpleName() + "." + pd.getName() + "(" + pd.getPropertyType() + ")");
							continue;
						}
						conversionContext.put(propValue, true);
						propValue = toMap(conversionContext, propValue);
					}
					LOG.debug(" - converting a bean property ... " + targetClass.getSimpleName() + "." + pd.getName() + "(" + pd.getPropertyType() + ")");
					map.put(propName, propValue);
				}
			}
			return map;
		}
	}
}
